/* This code sends data over the serial port.
 *
 * Communication protocol:
 *  - A data packet is made of a 16-bit length, 16-bit
 *    checksum (a simple XOR), and at most 512 16-bit words.
 *  - The remote side (=Shimmer) sends a ASK to request for a
 *    data packet.
 *  - The host sends the packet, the remote side replies
 *    with ACK if everything was ok, NACK otherwise.
 *  - Text messages can be sent from the remote side, starting
 *    with START, ending with END.
 */

#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <stdlib.h>
#include <ctype.h>
#include "validation.h"

offset_t offset = 0;
offset_t last_offset = 0x7FFFFFFFL; /* Last non-circular offset (EOF) */

int initport(int fd) {
	struct termios newtio;
	newtio.c_cflag = B38400 | CS8 | CLOCAL | CREAD;
	newtio.c_iflag = IGNPAR;
	newtio.c_oflag = 0;
	newtio.c_lflag = 0;
	newtio.c_cc[VMIN]=1;
	newtio.c_cc[VTIME]=0;
	tcflush(fd, TCIFLUSH);
	tcsetattr(fd,TCSANOW,&newtio);

	return 1;
}

#define ASK '?'
#define ACK 'A'
#define NACK 'N'
#define RESET 'R'
#define START '*' /* Start of a message */
#define END '%' /* End of the message */
#define DONE 'D'

#define RS_BLOCK 512

struct datablk {
	int16_t length;
	int16_t csum;
	int16_t buffer[RS_BLOCK];
} __attribute__((__packed__));

void handlemsg(int fd) {
	char readbuf[128];
	int pos = 0;
	int i;

	while (1) {
		i = read(fd, readbuf+pos, 1);
		if (i > 0) {
			if (readbuf[pos] == END) {
				readbuf[pos] = 0;
				break;
			}
		} else if (i == 0) {
			continue;
		} else {
			perror("Error while reading...\n");
			close(fd);
			exit(-1);
		}
		pos++;
	}

	fprintf(stderr, "%s%s", readbuf, readbuf[pos-1] == '\n' ? "" : "\n");

	if (readbuf[0] == 'P') {
		offset_t offset;
		enum wave wave;
		enum type type;

		switch (readbuf[1]) {
		case 'R': wave = qrs_wave; break;
		case 'P': wave = p_wave; break;
		case 'T': wave = t_wave; break;
		default: fprintf(stderr, "Not a valid point...\n"); return;
		}
		
		switch (readbuf[2]) {
		case 'o': type = type_onset; break;
		case 'p': type = type_peak; break;
		case 'e': type = type_end; break;
		case 's': type = type_sec_peak; break;
		default: fprintf(stderr, "Not a valid point...\n"); return;
		}
		
		if (readbuf[3] != ':') {
			fprintf(stderr, "Not a valid point...\n");
			return;
		}

		offset = atoi(&readbuf[4]);

		add_point(offset, wave, type);
	}
}

/* Wait for up to 2 specific types of answers, put 0 in a2 if you're not interested */
char waitanswer(int fd, char a1, char a2) {
	char readbuf[1];
	int i;

	while (1) {
		i = read(fd, readbuf, 1);
		if (i > 0) {
			if (readbuf[0] == START) {
				handlemsg(fd);
				continue;
			}
			if (readbuf[0] == a1) {
				return a1;
			} else if (a2 && readbuf[0] == a2) {
				return a2;
			} else {
				fprintf(stderr, "Unexpected (%d/%c)\n", readbuf[0] & 0xff, isprint(readbuf[0]) ? readbuf[0] : '?');
			}
		} else if (i == 0) {
			fprintf(stderr, "i==0...\n");
			continue;
		} else {
			perror("Error while reading...\n");
			close(fd);
			exit(-1);
		}
	}
}

void sendbuffer(int fd, char* buf, int length) {
	int i;

	fprintf(stderr, "Sending a block...\n");

	while (1) {
		waitanswer(fd, ASK, 0);
		
		for (i = 0; i < length; i++) {
			//fprintf(stderr, "%d\n", i);
			if (write(fd, &buf[i], 1) != 1) {
				fprintf(stderr, "Error while writing...\n");
				close(fd);
				exit(-1);
			}
//		sleep(1);
//		usleep(1);
		}

		fprintf(stderr, "Write done (%d bytes)\n", i);
		if (waitanswer(fd, ACK, NACK) == NACK) {
			fprintf(stderr, "NACK, retry...\n");
			handlemsg(fd);
			usleep(50000);
			continue;
		}
		break;
	}

	fprintf(stderr, "Done!\n");
}

int main(int argc, char **argv) {
	int fd;
	char buffer[128];
	struct datablk data;
	int lead = 0;

	if (argc != 5 && argc != 3) {
		fprintf(stderr, "Invalid number of arguments...\n");
		exit(-1);
	}

	fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY | O_NDELAY);
	if (fd == -1) {
		perror("open_port: Unable to open /dev/ttyUSB1 - ");
		return 1;
	} else {
		fcntl(fd, F_SETFL, 0);
	}
	
	initport(fd);


	for (lead = 0; lead < 2; lead++) {
		FILE* datafile;

		offset = 0;
		last_offset = 0x7FFFFFFFL;
		data.csum = 0;
		data.length = 0;

		if (!strcmp(argv[lead+1], "-"))
			datafile = stdin;
		else if (!strcmp(argv[lead+1], "0"))
			break;
		else
			datafile = fopen(argv[lead+1], "r");

		if (!datafile) {
			perror("Cannot open data file.");
			return 1;
		}		

		set_lead(lead);

		fprintf(stderr, "Reseting the chip\n");
		system("msp430-bsl -c /dev/ttyUSB0");

		fprintf(stderr, "Reseting performed, waiting for ack...\n");

		waitanswer(fd, RESET, 0);
		fprintf(stderr, "Reset OK...\n");

		while (1) {
			if (!fgets(buffer, 128, datafile)) {
				fprintf(stderr, "EOF\n");
				data.length += 2;
				sendbuffer(fd, (char*)&data, data.length*2);
				last_offset = offset = offset + data.length-2;
				break;
			}

			data.buffer[data.length] = atoi(buffer);
			data.csum = data.csum ^ data.buffer[data.length];
			data.length++;

			if (data.length == RS_BLOCK) {
				offset += RS_BLOCK;
				data.length += 2;
				sendbuffer(fd, (char*)&data, data.length*2);
				data.csum = 0;
				data.length = 0;
			}
		}

		waitanswer(fd, ASK, 0);
		data.length = 2;
		data.csum = 0;
		sendbuffer(fd, (char*)&data, data.length*2);
		waitanswer(fd, DONE, 0);
	}

	dump_points("r", qrs_wave, type_peak);
	dump_points("qrs", qrs_wave, type_sec_peak);
	dump_points("qrs_onset", qrs_wave, type_onset);
	dump_points("qrs_end", qrs_wave, type_end);
	dump_points("p", p_wave, type_peak);
	dump_points("psec", p_wave, type_sec_peak);
	dump_points("p_onset", p_wave, type_onset);
	dump_points("p_end", p_wave, type_end);
	dump_points("t", t_wave, type_peak);
	dump_points("tsec", t_wave, type_sec_peak);
	dump_points("t_onset", t_wave, type_onset);
	dump_points("t_end", t_wave, type_end);

	if (argc == 5) {
		read_validation(argv[3], atoi(argv[4]));
		//dump_val();
		check_validation();
	}

	close(fd);
	return 0;
}

